package com.dap.dao.mybatis;

import java.util.*;

import com.alibaba.fastjson.JSON;
import com.dap.cache.common.RedisAnnotationCache;
import com.dap.dao.model.BaseBeanQuery;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dap.dao.BasePo;
import com.dap.dao.DaoSupport;
import com.dap.dao.interceptor.SelectForUpdateHelper;
import com.dap.dao.interceptor.TotalRecordHelper;
import com.dap.dao.model.Page;


public class DaoSupportImpl extends SqlSessionDaoSupport implements DaoSupport {
    private final static Logger LOGGER = LoggerFactory.getLogger(DaoSupportImpl.class);
    private final static Logger ERROR_LOGGER = LoggerFactory.getLogger("error");

    protected DaoSupportImpl() {}

    private final static Map<String, Map<String, String>> propertyColumnMapper = new HashMap<String, Map<String, String>>();
    public static Map<String, Map<String, String>> getPropertyColumnMapper() {
        return propertyColumnMapper;
    }
    /** 用于加载属性对应的字段名称 */
    @SuppressWarnings("unused")
    private void initPropertyColumnMapper() {
        Collection<ResultMap> rms = getSqlSession().getConfiguration().getResultMaps();
        Iterator<ResultMap> iter = rms.iterator();
        while (iter.hasNext()) {
            Object object = iter.next();
            if (object instanceof ResultMap) {
                ResultMap rm = (ResultMap) object;
                List<ResultMapping> list = rm.getResultMappings();
                Map<String, String> map = new HashMap<String, String>();
                for (ResultMapping r : list) {
                    map.put(r.getProperty(), r.getColumn());
                }

                propertyColumnMapper.put(rm.getId(), map);
            }
        }
    }

    private <E> List<E> selectListInternal(String statement, Object parameter) {
        List<E> result = getSqlSession().selectList(statement, parameter);
        if (result.size() > 2000 || (Thread.currentThread().getName().indexOf("_Worker-")<0 && result.size() > 500)) {
            ERROR_LOGGER.error("return too much data, result.size()=" + result.size()
                               + ", statement=" + statement
                               + ", parameter=" + JSON.toJSONString(parameter),
                               new Exception(""));
        }
        return result;
    }




    @Override
    public Integer count(Object parameter) {
        return count(parameter.getClass(), parameter);
    }
    @Override
    public Integer count(Class<?> namespaceClass, Object parameter) {
        String className = namespaceClass.getName();
        return getSqlSession().selectOne(className + POSTFIX_COUNT, parameter);
    }
    @Deprecated
    @Override
    public Integer count(String statementPostfix, Object parameter) {
        return getSqlSession().selectOne(statementPostfix, parameter);
    }
    @Override
    public Integer count(Class<?> namespaceClass, String methodName, Object parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().selectOne(statementPostfix, parameter);
    }
    @Override
    public Integer countForUpdate(Object parameter) {
        return countForUpdate(parameter.getClass(), parameter);
    }
    @Override
    public Integer countForUpdate(Class<?> namespaceClass, Object parameter) {
        return countForUpdate(namespaceClass, POSTFIX_COUNT.substring(1), parameter);
    }
    @Override
    public Integer countForUpdate(Class<?> namespaceClass, String methodName, Object parameter) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            String statementPostfix = namespaceClass.getName() + "." + methodName;
            return getSqlSession().selectOne(statementPostfix, parameter);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }



    @Override
    public <T extends BasePo> T selectOne(T entity) {
        return selectOne((Class<T>)entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> T selectOne(Class<T> namespaceClass, T entity) {
        String className = namespaceClass.getName();
        return getSqlSession().selectOne(className + POSTFIX_SELECTONE, entity);
    }
    @Override
    @Deprecated
    public <T> T selectOne(String statementPostfix, Object parameter) {
        return getSqlSession().selectOne(statementPostfix, parameter);
    }
    @Override
    public <T> T selectOne(Class<?> namespaceClass, String methodName, Object parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().selectOne(statementPostfix, parameter);
    }
    @Override
    public <ResultType extends BasePo> ResultType selectOne(Class<?> namespaceClass, String methodName, Map<String, Object> parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return (ResultType) getSqlSession().selectOne(statementPostfix, parameter);
    }

    @Override
    public <T extends BasePo> T selectForUpdate(T parameter) {
        return selectForUpdate((Class<T>) parameter.getClass(), parameter);
    }
    @Override
    public <T extends BasePo> T selectForUpdate(Class<T> namespaceClass, T parameter) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            return selectOne(namespaceClass, parameter);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }
    @Override
    @Deprecated
    public <T> T selectForUpdate(String statementPostfix, Object parameter) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            return selectOne(statementPostfix, parameter);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }
    @Override
    public <T> T selectForUpdate(Class<?> namespaceClass, String methodName, Object parameter) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            return selectOne(namespaceClass, methodName, parameter);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }



    @Override
    public <T extends BasePo> int insert(T entity) {
        return insert((Class<T>) entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> int insert(Class<T> namespaceClass, T entity) {
        String className = namespaceClass.getName();
        return getSqlSession().insert(className + POSTFIX_INSERT, entity);
    }
    @Override
    public <T extends BasePo> int insert(String methodName, T entity) {
        String statementPostfix = entity.getClass().getName() + "." + methodName;
        return getSqlSession().insert(statementPostfix, entity);
    }
    @Override
    public <T extends BasePo> int insert(Class<?> namespaceClass, String methodName, T entity) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().insert(statementPostfix, entity);
    }
    @Override
    public int insert(Class<?> namespaceClass, String methodName, Map<String, Object> parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().insert(statementPostfix, parameter);
    }
    @Override
    public <T extends BasePo> int insert(Collection<T> collection) {
        return insert(POSTFIX_BATCH_INSERT.substring(1), collection);
    }
    @Override
    public <T extends BasePo> int insert(String methodName, Collection<T> collection) {
        if (null == collection || collection.isEmpty()) {
            return 0;
        }

        Map<String, Collection<T>> parameter = new HashMap<>();
        parameter.put(BATCH_INSERT_PARAMETER_NAME, collection);

        String className = collection.iterator().next().getClass().getName();

        return getSqlSession().insert(className + "." + methodName, parameter);
    }
    @Override
    public <T extends BasePo> int insert(Class<T> namespaceClass, Collection<T> collection) {
        return insert(namespaceClass, POSTFIX_BATCH_INSERT.substring(1), collection);
    }
    @Override
    public <T extends BasePo> int insert(Class<T> namespaceClass, String methodName, Collection<T> collection) {
        if (null == collection || collection.isEmpty()) {
            return 0;
        }

        Map<String, Collection<T>> parameter = new HashMap<>();
        parameter.put(BATCH_INSERT_PARAMETER_NAME, collection);

        String className = namespaceClass.getName();

        return getSqlSession().insert(className + "." + methodName, parameter);
    }



    @Override
    @Deprecated
    public <T extends BasePo> int update(T entity) {
        String className = entity.getClass().getName();
        return getSqlSession().update(className + POSTFIX_UPDATE, entity);
    }
    @Override
    public <T extends BasePo> int updateById(T entity) {
        return updateById((Class<T>) entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> int updateById(Class<T> namespaceClass, T entity) {
        String className = namespaceClass.getName();
        return getSqlSession().update(className + POSTFIX_UPDATE, entity);
    }

    @Override
    @Deprecated
    public <T extends BasePo> int update(T setParameter, T whereParameter) {
        return updateByWhereSet((Class<T>)setParameter.getClass(), setParameter, whereParameter);
    }
    @Override
    public <T extends BasePo> int updateByWhereSet(Class<T> namespaceClass, T setParameter, T whereParameter) {
        return update(namespaceClass, POSTFIX_UPDATE_BY_ENTITY.substring(1), setParameter, whereParameter);
    }

    @Override
    @Deprecated
    public <T extends BasePo> int update(String statementPostfix, T setParameter, T whereParameter) {
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("s", setParameter);
        parameter.put("w", whereParameter);
        return update(statementPostfix, parameter);
    }
    @Override
    public <T extends BasePo> int update(Class<?> namespaceClass, String methodName, T setParameter, T whereParameter) {
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("s", setParameter);
        parameter.put("w", whereParameter);
        return update(namespaceClass, methodName, parameter);
    }

    @Override
    @Deprecated
    public <T extends BasePo> int update(String statementPostfix, T entity) {
        return getSqlSession().update(statementPostfix, entity);
    }
    @Override
    public <T extends BasePo> int update(Class<?> namespaceClass, String methodName, T entity) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().update(statementPostfix, entity);
    }

    @Override
    @Deprecated
    public int update(String statementPostfix, Map<String, Object> parameter) {
        return getSqlSession().update(statementPostfix, parameter);
    }
    @Override
    public int update(Class<?> namespaceClass, String methodName, Map<String, Object> parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return getSqlSession().update(statementPostfix, parameter);
    }



    @Override
    public <T extends BasePo> int delete(T entity) {
        return delete((Class<T>) entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> int delete(Class<T> namespaceClass, T entity) {
        String className = namespaceClass.getName();
        return getSqlSession().delete(className + POSTFIX_DELETE, entity);
    }
    @Override
    @Deprecated
    public int delete(String statementPostfix, Object parameter) {
        return getSqlSession().update(statementPostfix, parameter);
    }
    @Override
    public int delete(Class<?> namespaceClass, String methodName, Object parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return delete(statementPostfix, parameter);
    }
    @Override
    public <IdType> int batchDelete(Class<?> namespaceClass, Collection<IdType> ids) {
        String statementPostfix = namespaceClass.getName() + POSTFIX_BATCH_IDELETE;

        Map<String, Collection<IdType>> parameter = new HashMap<>();
        parameter.put(BATCH_DELETE_PARAMETER_NAME, ids);
        return getSqlSession().update(statementPostfix, parameter);
    }




    @Override
    public <T extends BasePo> List<T> selectList(T entity) {
        return selectList((Class<T>) entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> List<T> selectList(Class<T> namespaceClass, T entity) {
        String statementPostfix = namespaceClass.getName() + POSTFIX_SELECTLIST;
        return selectListInternal(statementPostfix, entity);
    }
    @Override
    @Deprecated
    public <T> List<T> selectList(String statementPostfix, Object parameter) {
        return selectListInternal(statementPostfix, parameter);
    }
    @Override
    public <T> List<T> selectList(Class<?> namespaceClass, String methodName, Object parameter) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return selectListInternal(statementPostfix, parameter);
    }
    @Override
    public <T extends BasePo> List<T> selectListForUpdate(T entity) {
        return selectListForUpdate((Class<T>) entity.getClass(), entity);
    }
    @Override
    public <T extends BasePo> List<T> selectListForUpdate(Class<T> namespaceClass, T entity) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            return selectList(namespaceClass, entity);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }
    @Override
    public <T> List<T> selectListForUpdate(Class<?> namespaceClass, String methodName, Object parameter) {
        try {
            SelectForUpdateHelper.setSelectForUpdate();
            return selectList(namespaceClass, methodName, parameter);
        } finally {
            SelectForUpdateHelper.cancelSelectForUpdate();
        }
    }

    @Override
    public <T extends BasePo> List<T> selectList(T entity, Integer pageIndex, Integer pageSize) {
        return selectList((Class<T>) entity.getClass(), entity, pageIndex, pageSize);
    }
    @Override
    public <T extends BasePo> List<T> selectList(Class<T> namespaceClass, T entity, Integer pageIndex, Integer pageSize) {
        return selectList(namespaceClass, POSTFIX_SELECTLIST.substring(1), entity, pageIndex, pageSize);
    }
    private <T> List<T> selectListInternal(String statementPostfix, Object parameter, Integer pageIndex, Integer pageSize) {
        if (null == pageIndex || 0 == pageIndex || null == pageSize || 0 == pageSize) {
            return selectListInternal(statementPostfix, parameter);
        }

        Boolean needTotalFlag = TotalRecordHelper.isNeadTotalRowCount();
        try {
            TotalRecordHelper.setNeedTotalRowCount(false);
            return getSqlSession().selectList(statementPostfix, parameter, new RowBounds(pageIndex, pageSize));
        } finally {
            // 需要重新设置是否需要总记录
            TotalRecordHelper.setNeedTotalRowCount(needTotalFlag);
        }
    }
    @Override
    @Deprecated
    public <T> List<T> selectList(String statementPostfix, Object parameter, Integer pageIndex, Integer pageSize) {
        return selectListInternal(statementPostfix, parameter, pageIndex, pageSize);
    }
    @Override
    public <T> List<T> selectList(Class<?> namespaceClass, String methodName, Object parameter, Integer pageIndex, Integer pageSize) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return selectListInternal(statementPostfix, parameter, pageIndex, pageSize);
    }


    @Override
    public <T extends BasePo> Page<T> selectQueryResult(T entity, Integer pageIndex, Integer pageSize) {
        return selectQueryResult((Class<T>) entity.getClass(), entity, pageIndex, pageSize);
    }
    @Override
    public <T extends BasePo> Page<T> selectQueryResult(Class<T> namespaceClass, T entity, Integer pageIndex, Integer pageSize) {
        String statementPostfix = namespaceClass.getName() + POSTFIX_SELECTLIST;
        return selectQueryResultInternal(statementPostfix, entity, pageIndex, pageSize);
    }
    private <T> Page<T> selectQueryResultInternal(String statementPostfix, Object parameter, Integer pageIndex, Integer pageSize) {
        if (null == pageIndex || 0 == pageIndex || null == pageSize || 0 == pageSize) {
            List<T> list = selectListInternal(statementPostfix, parameter);
            return new Page<>(list.size(), list.size(), 1, list);
        } else {
            Boolean needTotalFlag = TotalRecordHelper.isNeadTotalRowCount();
            try {
                TotalRecordHelper.setNeedTotalRowCount(true);
                List<T> resultList =
                        getSqlSession().selectList(statementPostfix, parameter, new RowBounds(pageIndex, pageSize));
                Long totalRecord = TotalRecordHelper.getTotalRecord();
                return new Page<>(totalRecord, pageSize, pageIndex, resultList);
            } finally {
                TotalRecordHelper.setNeedTotalRowCount(needTotalFlag);
            }
        }
    }
    @Override
    @Deprecated
    public <T> Page<T> selectQueryResult(String statementPostfix, Object parameter, Integer pageIndex, Integer pageSize) {
        return selectQueryResultInternal(statementPostfix, parameter, pageIndex, pageSize);
    }
    @Override
    public <T> Page<T> selectQueryResult(Class<?> namespaceClass, String methodName, Object parameter, Integer pageIndex, Integer pageSize) {
        String statementPostfix = namespaceClass.getName() + "." + methodName;
        return selectQueryResultInternal(statementPostfix, parameter, pageIndex, pageSize);
    }


    @Override
    public <T> List<T> queryListByBean(Class<?> namespaceClass, Object parameter) {
        String statementPostfix = namespaceClass.getName() + ".queryListByBean";
        return selectListInternal(statementPostfix, parameter);
    }
    @Override
    public <T, P extends BaseBeanQuery> Page<T> queryPageByBean(Class<?> namespaceClass, P parameter) {
        if (null == parameter.getPageIndex() || null == parameter.getPageSize()) {
            List<T> list = queryListByBean(namespaceClass, parameter);
            return new Page<>(list.size(), list.size(), 1, list);
        }

        int pageIndex = parameter.getPageIndex();
        parameter.setPageIndex(null);
        int pageSize = parameter.getPageSize();
        parameter.setPageSize(null);

        Boolean needTotalFlag = TotalRecordHelper.isNeadTotalRowCount();
        try {
            String statementPostfix = namespaceClass.getName() + ".queryListByBean";
            TotalRecordHelper.setNeedTotalRowCount(true);
            List<T> resultList =
                    getSqlSession().selectList(statementPostfix, parameter, new RowBounds(pageIndex, pageSize));
            Long totalRecord = TotalRecordHelper.getTotalRecord();
            return new Page<>(totalRecord, pageSize, pageIndex, resultList);
        } finally {
            TotalRecordHelper.setNeedTotalRowCount(needTotalFlag);
        }
    }
    @Override
    public Integer countByBean(Class<?> namespaceClass, Object parameter) {
        String statementPostfix = namespaceClass.getName() + ".countByBean";
        return this.getSqlSession().selectOne(statementPostfix, parameter);
    }
    @Override
    public int deleteByBean(Class<?> namespaceClass, Object parameter) {
        String statementPostfix = namespaceClass.getName() + ".deleteByBean";
        return getSqlSession().delete(statementPostfix, parameter);
    }




    /**
     * 下面的所有方法走缓存
     */
    @Override
    @Deprecated
    @RedisAnnotationCache
    public <T extends BasePo> List<T> selectListCached(String keyPrefix, Object key, Integer expiration, T entity) {
        return selectList(entity);
    }
}
